<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>jQuery 双向绑定 - 通用列表绑定</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <style>
    .user-item {
      margin-bottom: 10px;
      padding: 10px;
      border: 1px solid #ccc;
      border-radius: 5px;
    }
    .user-item input {
      margin-right: 10px;
    }
    button {
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div id="userList">
    <!-- 列表项模板 -->
    <div class="user-item" data-template>
      <input type="text" data-bind="item.name" placeholder="Name">
      <input type="text" data-bind="item.age" placeholder="Age">
      <button data-action="remove">Remove</button>
    </div>
  </div>
  <button data-action="add">Add User</button>

  <script>
    (function($) {
      // 定义双向绑定工具函数
      $.fn.twoWayBind = function(model, property, options) {
        const settings = $.extend({
          event: 'input', // 默认监听的事件类型
          updateView: function(element, value) {
            // 默认更新视图的逻辑
            if (element.is('input, textarea, select')) {
              element.val(value);
            } else {
              element.text(value);
            }
          },
          updateModel: function(element) {
            // 默认更新数据模型的逻辑
            if (element.is('input, textarea, select')) {
              return element.val();
            } else {
              return element.text();
            }
          }
        }, options);

        // 绑定视图到数据模型
        this.on(settings.event, function() {
          model[property] = settings.updateModel($(this));
        });

        // 绑定数据模型到视图
        const updateView = function() {
          settings.updateView($(this), model[property]);
        };
        updateView.call(this);

        // 监听数据模型变化
        if (model.__isObserved) return; // 避免重复监听
        Object.defineProperty(model, property, {
          get: function() {
            return this._value;
          },
          set: function(newValue) {
            this._value = newValue;
            $(this).trigger('modelChange', [newValue]);
          }
        });
        model.__isObserved = true;

        // 数据模型变化时更新视图
        $(this).on('modelChange', function(event, value) {
          settings.updateView($(this), value);
        });

        return this;
      };

      // 定义通用列表渲染函数
      $.fn.renderList = function(data, key, template, options) {
        const settings = $.extend({
          onAdd: function() {}, // 添加项时的回调
          onRemove: function() {} // 删除项时的回调
        }, options);

        const $container = this;
        const $template = $(template).clone().removeAttr('data-template');

        // 渲染列表
        function render() {
          $container.empty();
          data[key].forEach((item, index) => {
            const $item = $template.clone();
            bindItem($item, item, index);
            $container.append($item);
          });
        }

        // 绑定列表项
        function bindItem($item, item, index) {
          $item.find('[data-bind]').each(function() {
            const $element = $(this);
            const bindKey = $element.data('bind').replace('item', `${key}.${index}`);
            $element.twoWayBind(item, bindKey.split('.').pop());
          });

          // 绑定删除按钮
          $item.find('[data-action="remove"]').on('click', function() {
            data[key].splice(index, 1);
            render();
            settings.onRemove(item, index);
          });
        }

        // 初始化渲染
        render();

        // 绑定添加按钮
        $container.parent().find('[data-action="add"]').on('click', function() {
          data[key].push({});
          render();
          settings.onAdd(data[key][data[key].length - 1], data[key].length - 1);
        });

        return this;
      };
    })(jQuery);

    // 定义数据模型
    const data = {
      users: [
        { name: "John", age: 25 },
        { name: "Jane", age: 30 }
      ]
    };

    // 渲染列表
    $('#userList').renderList(data, 'users', '#userList [data-template]', {
      onAdd: function(item, index) {
        console.log('Added:', item, index);
      },
      onRemove: function(item, index) {
        console.log('Removed:', item, index);
      }
    });
  </script>
</body>
</html>